#include "LengthUnit.h"
#include "VolumeUnit.h"
#include "UnitRate.h"
#include "UnitOut.h"
#include "UnitVisitor.h"
#include <alogrithm>

using namespace std;


/////////////////////////////////////////////////////////////////////
Unit::Unit(int ratePerBaseUnit, string name)
        : ratePerBaseUnit(ratePerBaseUnit) , name(name)
{}

int Unit::getRatePerBaseUnit() const
{
    return ratePerBaseUnit;
}


/////////////////////////////////////////////////////////////////////
LengthUnit::LengthUnit(int ratePerBaseUnit, string name)
        : Unit(ratePerBaseUnit, name)
{}

#define DEF_GET_LENGTH_UNIT(unit, rate, displayName) \
    CONST_LENGTH_UNIT_REFERECE SINGLETON_LENGTH_UNIT(unit)\
    {   \
        static const LengthUnit unit(rate, displayName); \
        return unit; \
    }

DEF_GET_LENGTH_UNIT(Mile, MILE_RATE_PER_BASE, MILE_DISPLAY_NAME)
DEF_GET_LENGTH_UNIT(Yard, YARD_RATE_PER_BASE, YARD_DISPLAY_NAME)
DEF_GET_LENGTH_UNIT(Feet, FEET_RATE_PER_BASE, YARD_DISPLAY_NAME)

const LengthUnit& LengthUnit::getBaseUnit() const
{
    return getFeet();
}

/////////////////////////////////////////////////////////////////////
VolumeUnit::VolumeUnit(int ratePerBaseUnit, string name)
        : Unit(ratePerBaseUnit, name)
{
}

#define DEF_GET_VOLUME_UNIT(unit, rate, displayName) \
    CONST_VOLUME_UNIT_REFERECE SINGLETON_VOLUME_UNIT(unit) \
    {   \
        static const VolumeUnit unit(rate, displayName); \
        return unit; \
    }

DEF_GET_VOLUME_UNIT(Tsp, TSP_RATE_PER_BASE, TSP_DISPLAY_NAME)
DEF_GET_VOLUME_UNIT(Tbsp, TBSP_RATE_PER_BASE, TBSP_DISPLAY_NAME)
DEF_GET_VOLUME_UNIT(Oz, OZ_RATE_PER_BASE, OZ_DISPLAY_NAME)

const VolumeUnit& VolumeUnit::getBaseUnit() const
{
    return getTsp();
}

/////////////////////////////////////////////////////////////////////
namespace
{
    bool isBiggerThan(const Unit& lhs, const Unit& rhs)
    {
        return lhs.getRatePerBaseUnit() > rhs.getRatePerBaseUnit();
    }
}

void SequenceUnits::addUnit(const Unit& unit)
{
    units.push_back(unit);
}

void SequenceUnits::accept(const UnitVisitor& visitor)
{
    sort();
     
    for (Units::const_interator i = units.begin(), i != units.end(); i++)
        visitor.visit(*i);
}

void SequenceUnits::sort()
{
    std::sort(units.begin(), units.end(), isBiggerThan);
}